package com.easyaop.commons;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class MethodVo {
    private static final String[] EMPTY_PARAM_TYPES = new String[0];

    private static final ConcurrentHashMap<String, MethodVo> CACHE = new ConcurrentHashMap<>();

    // 建议改为asm的Type类型
    private String className;

    private String methodName;

    // 建议改为asm的Type[]
    private String[] paramTypes;

    int hash;

    String expression;

    public MethodVo(String expression) {
        this.expression = expression;

        /** 左括号 */
        int left = expression.indexOf('(');
        /** 右括号 */
        int right = expression.lastIndexOf(')');
        /** 包名开始的位置 */
        int packageStart = expression.substring(0, left).lastIndexOf(" ") + 1;

        String params = expression.substring(left + 1, right);
        if ("".equals(params)) {
            paramTypes = EMPTY_PARAM_TYPES;
        } else {
            paramTypes =
                    Arrays.stream(params.split(",")).map(String::trim).filter(s -> !s.isEmpty()).toArray(String[]::new);
        }

        String[] split = expression.substring(packageStart, left).split("\\.");
        methodName = split[split.length - 1];

        className = Arrays.stream(split).limit(split.length - 1).collect(Collectors.joining("."));
    }

    public MethodVo(String className, String methodName, String[] paramTypes) {
        this.className = className;
        this.methodName = methodName;
        this.paramTypes = paramTypes;
    }

    public MethodVo(Method method) {

        this.className = method.getDeclaringClass().getName();
        this.methodName = method.getName();
        this.paramTypes = Arrays.stream(method.getParameterTypes()).map(MethodVo::toString).toArray(String[]::new);

        this.expression = method.toString();
//        this.expression = className + "." + methodName + "(" + Arrays.stream(paramTypes).collect(Collectors.joining
//        (",")) + ")";
    }

    public String getClassName() {
        return className;
    }

    public String getMethodName() {
        return methodName;
    }

    public String[] getParamTypes() {
        return paramTypes;
    }

    public String getExpression() {
        return expression;
    }

    public void setExpression(String expression) {
        this.expression = expression;
        hash = 0;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        MethodVo methodVO = (MethodVo) o;
        return Objects.equals(className, methodVO.className) && Objects.equals(methodName, methodVO.methodName) && Objects.deepEquals(paramTypes, methodVO.paramTypes);
    }

    @Override
    public int hashCode() {
        if (hash == 0) {
            hash = Objects.hash(className + "-" + methodName);
//            hash = Objects.hash(className, methodName, Arrays.hashCode(paramTypes));
        }
        return hash;
    }

    public static MethodVo of(String expression) {
        return CACHE.computeIfAbsent(expression, MethodVo::new);
    }

    public static String[] getParamTypes(Class[] paramTypes) {
        return Arrays.stream(paramTypes).map(MethodVo::toString).toArray(String[]::new);
    }

    public static String toString(Class<?> clz) {
        if (clz.isArray()) {
            Class<?> componentType = clz.getComponentType();
            return toString(componentType) + "[]";
        } else {
            return clz.getName();
        }
    }
}
